package com.gopay.checkorder.service.impl.bcdl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.gopay.checkorder.dao.BcdlQueryBalanceDAO;
import com.gopay.checkorder.dao.BisBankOrderQueryDAO;
import com.gopay.checkorder.manager.BankOrderManager;
import com.gopay.checkorder.service.BankOrderService;
import com.gopay.checkorder.service.BcdlOrderSendBankService;
import com.gopay.checkorder.service.BcdlOrderService;
import com.gopay.checkorder.service.sentinel.BcdlOrderSentinel;
import com.gopay.common.constants.bank.BankConstants;
import com.gopay.common.constants.bank.BankOrderStatus;
import com.gopay.common.constants.bank.BankOrderType;
import com.gopay.common.constants.bank.BisBankBusinessProd;
import com.gopay.common.constants.bank.BisBankInterfaceChannel;
import com.gopay.common.constants.proccode.ProcCodeConstants;
import com.gopay.common.cps.manager.order.CpsGeneralMainOrderQueryManager;
import com.gopay.common.domain.bank.BankOrder;
import com.gopay.common.domain.bank.BcdlResult;
import com.gopay.common.domain.cps.vo.CheckOrderResult;
import com.gopay.common.exception.GopayException;
import com.gopay.common.order.dao.CommonQueryDAO;
import com.gopay.common.util.DateUtils;
import com.gopay.common.util.HttpTransport;
import com.gopay.common.util.StringTools;
import com.gopay.common.util.spring.SpringContextUtil;
import com.gopay.gass.service.GassPayRemoteService;

/**
 * 网联付款service实现
 * 
 * @BcdlOrderNuccPayServiceImpl.java
 * @author zhengxue
 * @2018-1-24 上午10:45:37 www.gopay.com.cn Inc.All rights reserved.
 */
@Service("bcdlOrderNuccPayService")
public class BcdlOrderNuccPayServiceImpl implements BcdlOrderService {
    private final static Logger logger = LoggerFactory.getLogger(BcdlOrderNuccPayServiceImpl.class);

    @Resource(name = "bcdlBankOrderService")
    private BankOrderService bcdlBankOrderService;
    private int MAX_SIZE = 100;
    @Resource(name = "cpsGeneralMainOrderQueryManager")
    protected CpsGeneralMainOrderQueryManager mainOrderQuery;
    @Resource(name = "bankOrderManager")
    protected BankOrderManager bankOrderManager;
    @Resource(name = "bisBankOrderQueryDAO")
    private BisBankOrderQueryDAO bisBankOrderQueryDAO;
    @Resource(name = "bcdlQueryBalanceDAO")
    private BcdlQueryBalanceDAO bcdlQueryBalanceDAO;
    @Resource(name = "commonQueryDAO")
    private CommonQueryDAO commonQueryDAO;
    @Resource(name = "bcdlConfig")
    private Map<String, String> bcdlConfig;
    @Resource(name = "gassPayService")
    private GassPayRemoteService gassPayService;

    @Override
    public void processNucc(String bank, String notifyUrl) {
        // modified by
        // zyt,2017-10-10,抽出公用的合法时间校验到bankOrderManager中，时间校验前置，防止查询db后又被时间拦截，浪费db与服务的io
        if (!bankOrderManager.isLegalTime(bank)) {
            return;
        }

        StopWatch sw = new StopWatch();
        sw.start();
        // List<BankOrder> sendOrders = getSendBankOrder(bank);
        List<String> sendOrders = getSendBankOrder(bank);
        sw.split();
        logger.error(bank + "-----------getSendBankOrder耗时=" + sw.getSplitTime());
        logger.error("-----------" + bank + "发往银行记录总数=" + sendOrders.size());

        List<BankOrder> checkOrders = getCheckOrder(bank, (MAX_SIZE - sendOrders.size()));
        sw.split();
        logger.error(bank + "-----------getCheckOrder耗时=" + sw.getSplitTime());
        sw.stop();
        logger.error("-----------" + bank + "对单记录总数=" + checkOrders.size());

        try {
            // 继续对账
            String beanName = getSendBankBeanName(bank);
            if (false == SpringContextUtil.containsBean(beanName)) {
                logger.info("spring bean does not exist: " + beanName);
                return;
            }

            BcdlOrderSendBankService sendBankService = (BcdlOrderSendBankService) SpringContextUtil.getBean(beanName);
            HttpTransport notifyGass = new HttpTransport();
            notifyGass.setSendEncoding("UTF-8");
            for (String bankOrderId : sendOrders) {
                BcdlResult result = null;
                BankOrder bankOrder = bisBankOrderQueryDAO.find(BankOrder.class, bankOrderId);
                try {
                    // 非生产环境、非一分钱交易直接置为“99”失败
                    if (!"prod".equals(bcdlConfig.get("sys.id"))
                            && !"0.01".equals(bankOrder.getSendBankAmt().toString())) {
                        bankOrderManager.updateFail(bankOrder);
                    } else {
                        // logger.error("插入发往银行日志--开始,gopayOrderId="+bankOrder.getOrderId()
                        // + ",bank=" + bank);
                        // bankOrderManager.saveBcdlSendBankLog(bankOrder);//插入发往银行日志
                        // by lizheng 20170719
                        // logger.error("插入发往银行日志--结束,gopayOrderId="+bankOrder.getOrderId()
                        // + ",bank=" + bank);
                        logger.error("银企直连 发送银行--开始 ，orderId=" + bankOrder.getOrderId() + ",bank=" + bank);
                        result = sendBankService.sendBank(bankOrder);
                        logger.error("银企直连 发送银行--结束 ，orderId=" + bankOrder.getOrderId() + ",bank=" + bank
                                + ",resultCode=" + result.getResultCode());

                        Map<String, String> param = new HashMap<String, String>();
                        if (ProcCodeConstants.PROC_CODE_100E6027.equals(result.getResultCode())) {
                            param.put("resultCode", "2");
                            param.put("resultMsg", result.getErrorMsg());
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "1");
                            param.put("gopayOrderId", bankOrder.getOrderId());
                            // add by zyt,2017-10-13 ,是否需要做失败的拒绝账务，
                            if (result.isNeedRefund()) {
                                logger.info("发起失败拒绝账务：orderId:" + bankOrder.getOrderId());
                                param.put("needRefund", "1");
                            }
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);
                        } else if (ProcCodeConstants.PROC_CODE_100S1000.equals(result.getResultCode())) {
                            param.put("resultCode", "1");
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "1");
                            param.put("gopayOrderId", bankOrder.getOrderId());
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);
                        } else {
                            param.put("resultCode", "3");
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "1");
                            param.put("gopayOrderId", bankOrder.getOrderId());
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);
                        }

                        /**
                         * 招商银行，一次请求后暂停2s，中行，一次请求后暂停5s
                         */
                        if ("CMB".equalsIgnoreCase(bank)) {
                            logger.error("CMB sendbank sleep startTime:" + System.currentTimeMillis());
                            sleep(2000);
                            logger.error("CMB sendbank sleep endTime:" + System.currentTimeMillis());
                        } else if ("BOC".equalsIgnoreCase(bank)) {
                            logger.error("BOC sendbank sleep startTime:" + System.currentTimeMillis());
                            sleep(5000);
                            logger.error("BOC sendbank sleep endTime:" + System.currentTimeMillis());
                        }
                    }
                } catch (Exception e) {
                    logger.error("发送银行异常 ，orderId=" + bankOrder.getOrderId() + ",bank=" + bank, e);
                }
            }

            for (BankOrder bankOrder : checkOrders) {
                try {

                    // 非生产环境、非一分钱交易直接置为“99”失败
                    if (!"prod".equals(bcdlConfig.get("sys.id"))
                            && !"0.01".equals(bankOrder.getSendBankAmt().toString())) {
                        bankOrderManager.updateFail(bankOrder);
                    } else {

                        logger.error("银企直连 对单--开始 ，orderId=" + bankOrder.getOrderId() + ",bank=" + bank);
                        CheckOrderResult result = checkOrder(bankOrder, BankOrderStatus.AUTO_CHECK_SUCCESS);
                        logger.error("银企直连 对单--结束 ，orderId=" + bankOrder.getOrderId() + ",ErrCode="
                                + result.getErrCode() + ",isSuccess=" + result.isSuccess());

                        Map<String, String> param = new HashMap<String, String>();
                        if (ProcCodeConstants.PROC_CODE_100E6027.equals(result.getErrCode())) {
                            param.put("resultCode", "2");
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "2");
                            param.put("resultMsg", result.getErrMsg());
                            param.put("gopayOrderId", bankOrder.getOrderId());

                            // add by zyt,2017-10-13 ,是否需要做失败的拒绝账务，
                            if (result.isNeedRefund()) {
                                logger.info("发起失败拒绝账务：orderId:" + bankOrder.getOrderId());
                                param.put("needRefund", "1");
                            }
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);

                        } else if (result.isSuccess()) {
                            param.put("resultCode", "1");
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "2");
                            param.put("gopayOrderId", bankOrder.getOrderId());
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);
                        } else {
                            param.put("resultCode", "3");
                            // delete by zyt,2017-8-4，删除remark标志
                            // param.put("remark", "2");
                            param.put("gopayOrderId", bankOrder.getOrderId());
                            /*
                             * notifyGass.setUrl(bankOrder.getRemark2());
                             * notifyGass.submit(param);
                             */// 生产优化10期 lx httpclient 替换为 hessian
                            gassPayService.bcdlNotifyGass(param);
                        }

                        /**
                         * 招商银行，一次请求后暂停2s，中行，一次请求后暂停5s
                         */
                        if ("CMB".equalsIgnoreCase(bank)) {
                            logger.error("CMB check sleep startTime:" + System.currentTimeMillis());
                            sleep(2000);
                            logger.error("CMB check sleep endTime:" + System.currentTimeMillis());
                        } else if ("BOC".equalsIgnoreCase(bank)) {
                            logger.error("BOC check sleep startTime:" + System.currentTimeMillis());
                            sleep(5000);
                            logger.error("BOC check sleep endTime:" + System.currentTimeMillis());
                        }
                    }
                } catch (Exception e) {
                    logger.error("对单异常 ，orderId=" + bankOrder.getOrderId() + ",bank=" + bank, e);
                }

            }

//            // 查询账户余额
//            List<BcdlQueryBalance> bcdlQuerys = getBcdlQueryOrder(bank);
//            if (bcdlQuerys == null || bcdlQuerys.size() <= 0) {
//                logger.error("bcdl queryBalance orders is null");
//                return;
//            }
//            BcdlQueryBalance bcdlQuery = bcdlQuerys.get(0);
//            logger.error("银企直连 查询余额--开始 ，orderId=" + bcdlQuery.getQueryOrderId() + ",bank=" + bank);
//            BcdlResult result = sendBankService.queryBalance(bcdlQuery);
//            logger.error("银企直连 查询余额--结束 ，orderId=" + bcdlQuery.getQueryOrderId() + ",ResultCode="
//                    + result.getResultCode() + ",isSuccess=" + result.isSuccess());
//
//            notifyCps(bank, notifyUrl, result);
        } catch (Exception e) {
            logger.error("checkOrder error message: ,bank=" + bank, e);
        }
    }

    @Override
    public CheckOrderResult checkOrder(BankOrder bankOrder, String target) {
        CheckOrderResult checkOrderResult = new CheckOrderResult();
        checkOrderResult = bcdlBankOrderService.checkOrder(bankOrder, target);
        return checkOrderResult;
    }

    public List<String> getSendBankOrder(String bank) {
        List<Object[]> list = getCheckBcdlOrderByBank(bank, BankOrderStatus.ACCEPTED, 1, MAX_SIZE);
        List<String> orderIds = new ArrayList<String>();
        for (Object[] bankOrderId : list) {
            orderIds.add(StringTools.checkNulltoString(bankOrderId[0]));
        }
        return orderIds;
    }

    public List<Object[]> getCheckBcdlOrderByBank(String bank, String orderStatus, int pageIndex, int pageSize) {
        StringBuffer sql = new StringBuffer(250);
        sql.append("select b.GOPAY_OUT_ORDER_ID,b.GOPAY_ORDER_ID from BIS_BANK_MAIN_ORDER b ");
        sql.append(" where b.ORDER_STATUS = :orderStatus ");
        sql.append(" and b.BANK_BIZ_PROD_CODE = :bankBizProdCode ");
        sql.append(" and b.ORDER_TYPE = :orderType ");
        // sql.append(" and b.GOPAY_BANK_CODE = :bankCode ");
        sql.append(" and b.INTRFACE_CHANNEL_CODE = :interfaceChennelCode ");
        sql.append(" and b.TOTAL_CHECK_TIMES <= :totalCheckTimes ");
        sql.append(" and b.SEND_BANK_TM >=to_date(:beginTime ,'yyyyMMddHH24miss') ");
        // 修改T+1插队到T+0问题，改成订单号降序
        sql.append(" order by b.TOTAL_CHECK_TIMES, b.GOPAY_ORDER_ID desc ");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("orderStatus", orderStatus);
        params.put("bankBizProdCode", BisBankBusinessProd._09.value);
        params.put("orderType", BankOrderType.GOPAY_BIS_ORDER);
        // params.put("bankCode", bank);
        params.put("interfaceChennelCode", BisBankInterfaceChannel._18.value);
        params.put("totalCheckTimes", 7);
        params.put("beginTime", recentlyTime());
        List<Object[]> list = commonQueryDAO.findBySql(sql.toString(), params, pageIndex, pageSize);
        return list;
    }

    private String recentlyTime() {
        Calendar calendar = Calendar.getInstance();

        // 开始时间--最近1天
        calendar.setTime(new Date());
        calendar.add(Calendar.DAY_OF_MONTH, -3);
        String beginTime = DateUtils.format(calendar.getTime(), "yyyyMMddHHmmss");

        return beginTime;
    }

    public List<BankOrder> getCheckOrder(String bank, int max) {
        int count = getOrderCount(BankOrderStatus.PROCESS, bank);
        int pageIndex = 1;
        int pageSize = 0;

        List<BankOrder> list = new ArrayList<BankOrder>();
        if (max <= 0) {
            return list;
        }
        if (count > max) {
            pageSize = max;
        } else {
            pageSize = count;
        }
        list = getProcessOrder(bank, BankOrderStatus.PROCESS, pageIndex, pageSize);
        return list;
    }

    private int getOrderCount(String orderStatus, String bank) {
        int resCount = 0;
        if (bank.equals(BankConstants.BANK_LOGO_NUCCPAY)) {
            resCount = bisBankOrderQueryDAO.getCheckBcdlOrderCountByChannel(BisBankInterfaceChannel._18.value,
                    orderStatus);
        } else {
            resCount = bisBankOrderQueryDAO.getCheckBcdlOrderCountByBank(bank, orderStatus);
        }
        if (resCount > MAX_SIZE) {
            resCount = MAX_SIZE;
        }
        return resCount;
    }

    /**
     * 分页查询对账记录
     * 
     * @param bank
     * @param orderStatus
     * @param pageIndex
     * @param pageSize
     * @return
     */
    private List<BankOrder> getProcessOrder(String bank, String orderStatus, int pageIndex, int pageSize) {
        List<BankOrder> bankOrders = null;
        if (bank.equals(BankConstants.BANK_LOGO_NUCCPAY)) {
            logger.error("---checkbcdlbank---nuccpay" + bank);
            bankOrders = bisBankOrderQueryDAO.getCheckBcdlOrderByChannel(BisBankInterfaceChannel._18.value,
                    orderStatus, pageIndex, pageSize);
        } else {
            logger.error("---checkbcdlbank---other" + bank);
            bankOrders = bisBankOrderQueryDAO.getCheckBcdlOrderByBank(bank, orderStatus, pageIndex, pageSize);
        }
        return bankOrders;
    }
    
    private String getSendBankBeanName(String bankCode) throws GopayException {
        if(BankConstants.BANK_LOGO_NUCCPAY.equals(bankCode)){
            return "NUCCPAY-BCDL";
        }
        String beanName = bankCode + "-BCDL";
        return beanName;
    }
    
    private void sleep(long pausesTime) {
        try {
            Thread.sleep(pausesTime);
        } catch (InterruptedException e) {
            logger.error(getClass().getName() + " CMB doTask fail.", e);
        }
    }

    @Override
    public void process(String bankCode, String notifyUrl, BcdlOrderSentinel sentinel) {
        // TODO Auto-generated method stub
        
    }

}
